package com.ruoyi.system.protocol.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.ClassesConstants;
import com.ruoyi.system.domain.DO.RetrievalSearchResult;
import com.ruoyi.system.protocol.IDynamicCacheService;
import com.ruoyi.system.protocol.IDynamicSearchService;
import com.ruoyi.system.utils.RetrievalUtil;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class DynamicSearchJuheServiceImpl implements IDynamicSearchService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicSearchJuheServiceImpl.class);



    @Autowired
    private RestHighLevelClient juheTransportClient;
    private static RestHighLevelClient client;

    @Autowired
    private IDynamicCacheService dynamicCache;

    @Autowired
    private RetrievalUtil retrievalUtil;

    @PostConstruct
    public void init() {
        client = this.juheTransportClient;
    }
    //带参数的构造函数
    public void setClient(RestHighLevelClient client) {
        this.client = client;
    }
    //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑========初始化elasticSearch服务结束========↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑


    @Override
    public boolean existsIndex(String index) {
        boolean exists = false;
        try{
            GetIndexRequest request = new GetIndexRequest(index);
            exists= client.indices().exists(request, RequestOptions.DEFAULT);
        }catch (Exception e){
            LOGGER.error("判断索引{}是否存在,出现异常!:{}", index, e.getMessage());
        }
        return exists;
    }

    @Override
    public synchronized boolean createIndex(String index) {
        if (!existsIndex(index)) {
            LOGGER.info("Index is not exits!");
        }else{
            LOGGER.info("Index is exits!");
            return true;
        }
        boolean success = false;
        try{
            CreateIndexRequest request = new CreateIndexRequest(index);
            request.settings(getElasticSearchSetting());
//            request.mapping(getElasticSearchMapping());
            CreateIndexResponse indexResponse = client.indices().create(request, RequestOptions.DEFAULT);
            success = indexResponse.isAcknowledged();
            LOGGER.info("创建索引{}成功:{}?", index, success);
        }catch (Exception e){
            LOGGER.error("创建索引{},出现异常!:{}", index, e.getMessage());
        }
        return success;
    }

    @Override
    public boolean deleteIndex(String index) {
        if (!existsIndex(index)) {
            LOGGER.info("Index is not exits!");
        }
        boolean success = false;
        try{
            DeleteIndexRequest request = new DeleteIndexRequest(index);
            AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
            success = delete.isAcknowledged();
            LOGGER.info("删除索引{}成功:{}?", index, success);
        }catch (Exception e){
            LOGGER.error("删除索引{},出现异常!:{}", index, e.getMessage());
        }
        return success;
    }

    @Override
    public Boolean existsDocument(String index, String id) {
        boolean exists = false;
        try{
            GetRequest request = new GetRequest(index, id);
            exists = client.exists(request, RequestOptions.DEFAULT);
        }catch (Exception e){
            LOGGER.error("判断索引{}中,文档ID[{}]是否存在,出现异常!:{}", index, id, e.getMessage());
        }
        return exists;
    }

    @Override
    public void createDocument(Object object, String index, String id) {
        if (!existsIndex(index)) {
            createIndex(index);
        }
        try{
            //声明要保存到那个索引库
            IndexRequest request = new IndexRequest(index);
            request.id(id).timeout("1s");
            //给请求放入数据
            request.source(JSON.toJSONString(object), XContentType.JSON);
            //执行请求
            IndexResponse resp = client.index(request, RequestOptions.DEFAULT);
            LOGGER.info("createDocument response status:{},id:{}", resp.status().getStatus(), resp.getId());
        }catch (Exception e){
            LOGGER.error("索引{}中,创建文档[{}],出现异常!:{}", index, id, e.getMessage());
        }

    }

    @Override
    public void batchCreateDocument(List<Object> objList, String index) {
        if (!existsIndex(index)) {
            createIndex(index);
        }
        try{
            //1.创建批量导入数据
            BulkRequest bulkRequest = new BulkRequest();
            //设置多长时间导入一次
            bulkRequest.timeout("10s");
            objList.forEach(item -> {
                String jsonString = JSON.toJSONString(item);
                JSONObject jsonObject = JSON.parseObject(jsonString);
                String id = jsonObject.get("id").toString();
                if (StringUtils.isNoneBlank(id)) {
                    //如果需要做批量删除或者批量更新，修改这里请求即可
                    bulkRequest.add(
                            new IndexRequest(index)
                                    //不填id时将会生成随机id
                                    .id(id)
                                    .source(jsonString, XContentType.JSON)
                    );
                }
            });
            //4.执行请求
            BulkResponse resp = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            //5.响应 判断是否执行成功
            LOGGER.info("Es - batchCreateDocument response status:{}", resp.status().getStatus());
        }catch (Exception e){
            LOGGER.error("索引{}中,批量创建文档,出现异常!:{}", index, e.getMessage());
        }
    }

    @Override
    public void deleteDocument(String index, String id) {
        try{
            if(existsDocument(index, id)){
                DeleteRequest request = new DeleteRequest(index, id);
                DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
                LOGGER.info("deleteDocument response status:{},id:{}", delete.status().getStatus(), delete.getId());
            }
        }catch (Exception e){
            LOGGER.error("索引{}中,删除文档[{}],出现异常!:{}", index, e.getMessage());
        }
    }

    @Override
    public void deleteDocument(String index, List<String> ids) {
        try{
            QueryBuilder removeIdQueryBuilder = QueryBuilders.termsQuery("id", ids);
            deleteQueryDocument(index, removeIdQueryBuilder);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void deleteQueryDocument(String index, QueryBuilder query) {

        try{
            DeleteByQueryRequest request = new DeleteByQueryRequest(index);
            request.setQuery(query);
            BulkByScrollResponse resp = client.deleteByQuery(request, RequestOptions.DEFAULT);
            LOGGER.info("deleteDocument response deleted:{}  total:{}", resp.getStatus().getDeleted(), resp.getStatus().getTotal());
        }catch (Exception e){
            LOGGER.error("索引{}中,删除文档[{}],出现异常!:{}", index, e.getMessage());
        }
    }

    @Override
    public void updateDocument(Object object, String index, String id) {
        try{
            //声明索引库
            UpdateRequest request = new UpdateRequest(index, id);
            request.id(id).timeout("1s");//设置修改的文档id和请求超时时间
            request.doc(JSON.toJSONString(object), XContentType.JSON);
            //执行修改  修改的时候，如果对象中某个字段没有给值，那么也会修改成默认值
            UpdateResponse update = client.update(request, RequestOptions.DEFAULT);
            LOGGER.info("updateDocument response status:{},id:{}", update.status().getStatus(), update.getId());
        }catch (Exception e){
            LOGGER.error("索引{}中,修改文档[{}],出现异常!:{}", index, e.getMessage());
        }
    }

    @Override
    public Map<String, Object> queryDocument(String index, String id) {
        Map<String, Object> sourceAsMap = new HashMap<>();
        try{
            GetRequest request = new GetRequest(index, id);
            GetResponse resp = client.get(request, RequestOptions.DEFAULT);
            sourceAsMap = resp.getSourceAsMap();
        }catch (Exception e){
            LOGGER.error("索引{}中,按ID查询文档[{}],出现异常!:{}", index, e.getMessage());
        }
        return sourceAsMap;
    }

    @Override
    public HashMap<String, Object> search(String index, QueryBuilder query, QueryBuilder filterQuery, String[] showFields, Map<String, String> sortField, String[] setHighlightFields, int startPage, int pageSize) {

        if (!existsIndex(index)) {
            try{
                createIndex(index);
            }catch (Exception e){
                System.out.println("创建索引失败："+e.getMessage());
            }
        }
        HashMap<String, Object> r = new HashMap<String, Object>();
        //条件查询
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        if(pageSize <= 0){
            //最大无上限，但是建议设置为100，如有需要可以自行调整
            pageSize = 10000;
        }

        //分页
        searchSourceBuilder.from((startPage - 1) * pageSize);
        searchSourceBuilder.size(pageSize);

        if(query != null){
            searchSourceBuilder.query(query);
        }
        if(filterQuery != null){
            searchSourceBuilder.postFilter(filterQuery);
        }
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        //设置显示字段
        if(showFields != null && showFields.length > 0){
            searchSourceBuilder.fetchSource(showFields,null);
        }
        //设置排序
        if(sortField != null && sortField.size() > 0){
            sortField.forEach((key,val)->{
                SortOrder sortRule = SortOrder.DESC;
                if("asc".equals(val)){
                    sortRule = SortOrder.ASC;
                }
                searchSourceBuilder.sort(key, sortRule);
            });
        }

        //高亮
        if (setHighlightFields != null && setHighlightFields.length > 0) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            //多个高亮显示
            highlightBuilder.requireFieldMatch(false);
            //设置前缀
            highlightBuilder.preTags(ClassesConstants.SEARCH_HIGHLIGHT_PREFIX);
            //设置后缀
            highlightBuilder.postTags(ClassesConstants.SEARCH_HIGHLIGHT_SUFFIX);
            // 设置高亮字段
            for (String setHighlightField : setHighlightFields) {
                highlightBuilder.field(setHighlightField);
            }
            //下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
            //最大高亮分片数
            highlightBuilder.fragmentSize(800000);
            //从第一个分片获取高亮片段
            highlightBuilder.numOfFragments(0);
            searchSourceBuilder.highlighter(highlightBuilder);
        }

        //执行查询
        searchRequest.source(searchSourceBuilder);
        //如果不加此设置。 最大之后返回10000
        searchSourceBuilder.trackTotalHits(true);
        try{
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 获取查询总数
            LOGGER.info("Es - 查询总数{}。列表耗时{}秒", searchResponse.getHits().getTotalHits().value, searchResponse.getTook().getMillis() / 1000.0);
            long start = System.currentTimeMillis();
            List<Map<String, Object>> resList = setSearchResponse(searchResponse, setHighlightFields);
            System.out.println("转化耗时:"+(System.currentTimeMillis()-start));
            if (searchResponse.status().getStatus() == 200) {
                r.put("data", resList);
                r.put("time", searchResponse.getTook().getMillis() / 1000.0);
                r.put("count", searchResponse.getHits().getTotalHits().value);
                return r;
            }
        }catch (Exception e){
            LOGGER.error("索引{}中,按条件查询文档,出现异常!:{}", index, e.getMessage());
        }

        return null;
    }

    @Override
    public LinkedHashMap<String, LinkedHashMap<String, Object>> facet(String index, QueryBuilder query, QueryBuilder filterQuery, String[] fields, int pageSize) {
        if (!existsIndex(index)) {
            try{
                createIndex(index);
            }catch (Exception e){
                System.out.println("创建索引失败："+e.getMessage());
            }
        }
        if(pageSize <= 0){
            //最大无上限，但是建议设置为100，如有需要可以自行调整
            pageSize = 100;
        }
        LinkedHashMap<String, LinkedHashMap<String, Object>> resMap = new LinkedHashMap<>();
        //条件查询
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(query != null){
            boolQueryBuilder.must(query);
        }
        if(filterQuery != null){
            boolQueryBuilder.must(filterQuery);
        }
        searchSourceBuilder.query(boolQueryBuilder);



        for (String field : fields) {
            TermsAggregationBuilder termsAgg = AggregationBuilders.terms(field).field(field+".keyword").size(pageSize);
            searchSourceBuilder.aggregation(termsAgg);
        }
        //执行查询
        searchRequest.source(searchSourceBuilder);
        try{
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("Es - 分面耗时{}秒", searchResponse.getTook().getMillis() / 1000.0);
            LinkedHashMap<String, Object> aggMap = null;
            LinkedHashMap<String, Object> aggDataMap = null;
            LinkedHashMap<String, Object> aggCountMap = null;
            Aggregations aggregations = searchResponse.getAggregations();
            Map<String, Aggregation> asMap = aggregations.getAsMap();
            for(String key: asMap.keySet()){
                aggDataMap = new LinkedHashMap<>();
                Terms aggCount = aggregations.get(key);
                for (Terms.Bucket bucket : aggCount.getBuckets()) {
                    String keyAsString = bucket.getKeyAsString();
                    aggDataMap.put(keyAsString, bucket.getDocCount());
                }
                resMap.put(retrievalUtil.replaceRetrievalFieldReverse(key), aggDataMap);
            }
            if (searchResponse.status().getStatus() == 200) {
                return resMap;
            }
        }catch (Exception e){
            LOGGER.error("索引{}中,按条件分面,出现异常!:{}", index, e.getMessage());
        }
        return null;
    }

    @Override
    public Long searchCount(String index, QueryBuilder query, QueryBuilder filterQuery) {
        if (!existsIndex(index)) {
            try{
                createIndex(index);
            }catch (Exception e){
                System.out.println("创建索引失败："+e.getMessage());
            }
        }
        //条件查询
        CountRequest countRequest = new CountRequest(index);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(query != null){
            boolQueryBuilder.must(query);
        }
        if(filterQuery != null){
            boolQueryBuilder.must(filterQuery);
        }
        //执行查询
        countRequest.query(boolQueryBuilder);
        try{
            CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
            Long count = countResponse.getCount();
            return count;
        }catch (Exception e){
            LOGGER.error("索引{}中,按条件查询文档总数,出现异常!:{}", index, e.getMessage());
        }
        return null;
    }

    @Override
    public Map<String, Object> analysisQuery(String index, QueryBuilder query, AggregationBuilder aggregationBuilder) {
        if (!existsIndex(index)) {
            try{
                createIndex(index);
            }catch (Exception e){
                System.out.println("创建索引失败："+e.getMessage());
            }
        }
        //条件查询
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        if(query != null){
            searchSourceBuilder.query(query);
        }

        searchSourceBuilder.aggregation(aggregationBuilder);
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(0);
        searchSourceBuilder.trackTotalHits(true);
        searchRequest.source(searchSourceBuilder);

        Map<String, Object> aggregation = new HashMap<>();
        // 创建检索请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            if (searchResponse.status().getStatus() == 200) {
                // 处理聚合数据
                if (null != searchResponse.getAggregations()) {
                    List<Aggregation> Aggregations = searchResponse.getAggregations().asList();
                    for (Aggregation a : Aggregations) {
                        aggregation.putAll(retrievalUtil.formatAggregationData(a));
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return aggregation;
    }

    @Override
    public RetrievalSearchResult list(String index, QueryBuilder query, QueryBuilder filterQuery, String[] fields, Map<String, String> sortField, String[] setHighlightFields, int startPage, int pageSize) {
        HashMap<String, Object> search = search(index, query, filterQuery, fields, sortField, setHighlightFields, startPage, pageSize);
        Long total = 0L;
        List<Map<String, Object>> data = new ArrayList<>();
        try{
            total = Long.parseLong(search.get("count")+"");
            data = (List<Map<String, Object>>)search.get("data");
        }catch (Exception e){
            e.printStackTrace();
        }
        long pageCount = retrievalUtil.getPageCount(total, pageSize);
        return RetrievalSearchResult.success().records(data).total(total).current(startPage).size(pageSize).pages(pageCount).time(search.get("time"));
    }


    /**
     * 高亮结果集、查询字段回显 特殊处理
     *
     * @param searchResponse
     * @param setHighlightFields
     */
    private List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String[] setHighlightFields) {

        //解析结果
        ArrayList<Map<String, Object>> resList = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
            Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
            if (setHighlightFields != null && setHighlightFields.length > 0) {
                for (String setHighlightField : setHighlightFields) {
                    HighlightField field = highlightFields.get(setHighlightField);
                    if (field != null) {
                        Text[] fragments = field.getFragments();
                        setHighlightField = setHighlightField.replace(".keyword", "");
                        Object val = sourceAsMap.get(setHighlightField);
                        if(val instanceof List<?>){
                            //处理list字段高亮
                            List<String> n_field = (List<String>) val;
                            for (Text fragment : fragments) {
                                n_field.replaceAll(s -> {
                                    String originValue =
                                            fragment.toString()
                                                    .replace(ClassesConstants.SEARCH_HIGHLIGHT_PREFIX, "").replace(ClassesConstants.SEARCH_HIGHLIGHT_SUFFIX, "");
                                    if (Objects.equals(s, originValue)) {
                                        return fragment.toString();
                                    }
                                    return s;
                                });
                                sourceAsMap.put(setHighlightField, n_field);
                            }
                        }else{
                            String n_field = "";
                            for (Text str : fragments) {
                                n_field += str;
                            }
                            sourceAsMap.put(setHighlightField, n_field);
                        }
                    }
                }
            }
            resList.add(retrievalUtil.fieldReplaceList(sourceAsMap));
        }
        return resList;
    }





    //设置策略
    private XContentBuilder getElasticSearchSetting() throws IOException {
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.startObject("index");
            {
                builder.field("max_result_window", 10000 * 100);
                builder.field("number_of_replicas", "1");
                builder.field("number_of_shards", "5");
                builder.field("refresh_interval", "60s");
                builder.startObject("analysis");
                {
                    builder.startObject("analyzer");
                    {
                        //拼音分词
                        // text: 记录就是用来打破的
                        // analyzer: [ji, 记录就是用来打破的, jljsyldpd, lu, jiu, shi, yong, lai, da, po, de]
                        builder.startObject("pinyin_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "pinyin_tokenizer");
                        }
                        builder.endObject();

                        //hanlp smart
                        // text: 记录就是用来打破的
                        // analyzer: [记录, 就是, 用来, 打破, 的]
                        builder.startObject("hanlp_smart_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "hanlp_smart_tokenizer");
                        }
                        builder.endObject();

                        //hanlp max
                        // text: 记录就是用来打破的
                        // analyzer: [记录, 就是, 用来, 打破, 的]
                        builder.startObject("hanlp_max_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "hanlp_max_tokenizer");
                        }
                        builder.endObject();

                        //keyword+拼音全拼
                        // text: 记录就是用来打破的
                        // analyzer: [jilujiushiyonglaidapode]
                        builder.startObject("keyword_full_pinyin_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "keyword_full_pinyin_tokenizer");
                        }
                        builder.endObject();

                        //keyword+拼音首拼
                        // text: 记录就是用来打破的
                        // analyzer: [jljsyldpd]
                        builder.startObject("keyword_first_pinyin_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "keyword_first_pinyin_tokenizer");
                        }
                        builder.endObject();

                        //ik分词-最粗粒度
                        // text: 记录就是用来打破的
                        // analyzer: [记录, 就是, 用来, 打破, 的]
                        builder.startObject("ik_smart_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "ik_smart_tokenizer");
                        }
                        builder.endObject();

                        //ik分词-最细粒度
                        // text: 记录就是用来打破的
                        // analyzer: [记录, 就是, 用来, 打破, 的]
                        builder.startObject("ik_max_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "ik_max_tokenizer");
                        }
                        builder.endObject();

                        //ik+pinyin-最粗颗粒度
                        // text: 记录就是用来打破的
                        // analyzer: [ji, 记录, jl, lu, jiu, shi, 就是, js, yong, lai, 用来, yl, da, po, 打破, dp, de, 的, d]
                        builder.startObject("ik_smart_pinyin_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "ik_smart_pinyin_tokenizer");
                            builder.array("filter", "pinyin_first_letter_and_full_pinyin_filter");
                        }
                        builder.endObject();

                        //ik+pinyin-最细颗粒度
                        // text: 记录就是用来打破的
                        // analyzer: [ji, 记录, jl, lu, jiu, shi, 就是, js, yong, lai, 用来, yl, da, po, 打破, dp, de, 的, d]
                        builder.startObject("ik_max_pinyin_analyzer");
                        {
                            builder.field("type", "custom");
                            builder.field("tokenizer", "ik_max_pinyin_tokenizer");
                            builder.array("filter", "pinyin_first_letter_and_full_pinyin_filter");
                        }
                        builder.endObject();
                    }
                    builder.endObject();
                    builder.startObject("tokenizer");
                    {
                        builder.startObject("pinyin_tokenizer");
                        {
                            builder.field("type", "pinyin");
                            builder.field("keep_first_letter", true);  //设置为true话如：张三 会分词为 zs
                            builder.field("keep_separate_first_letter", false); //设置为true的话每个字的首字母都会进行分词
                            builder.field("keep_full_pinyin", true);
                            builder.field("keep_original", true);
                            builder.field("limit_first_letter_length", 16);
                            builder.field("lowercase", true);
                            builder.field("remove_duplicated_term", true);
                        }
                        builder.endObject();
                        builder.startObject("hanlp_smart_tokenizer");
                        {
                            builder.field("type", "hanlp_standard");

                        }
                        builder.endObject();
                        builder.startObject("hanlp_max_tokenizer");
                        {
                            builder.field("type", "hanlp_index");

                        }
                        builder.endObject();
                        builder.startObject("keyword_full_pinyin_tokenizer");
                        {
                            builder.field("type", "pinyin");
                            builder.field("keep_first_letter", false);
                            builder.field("keep_full_pinyin", false);
                            builder.field("keep_original", false);
                            builder.field("limit_first_letter_length", 16);
                            builder.field("lowercase", true);
                            builder.field("keep_none_chinese_in_first_letter", false);
                            builder.field("none_chinese_pinyin_tokenize", false);
                            builder.field("keep_none_chinese", true);
                            builder.field("keep_none_chinese_in_joined_full_pinyin", true);
                            builder.field("keep_separate_first_letter", false);
                            builder.field("keep_joined_full_pinyin", true);
                        }
                        builder.endObject();
                        builder.startObject("keyword_first_pinyin_tokenizer");
                        {
                            builder.field("type", "pinyin");
                            builder.field("keep_first_letter", true);
                            builder.field("keep_full_pinyin", false);
                            builder.field("keep_original", false);
                            builder.field("limit_first_letter_length", 16);
                            builder.field("lowercase", true);
                            builder.field("trim_whitespace", true);
                            builder.field("keep_none_chinese_in_first_letter", false);
                            builder.field("none_chinese_pinyin_tokenize", false);
                            builder.field("keep_none_chinese", true);
                            builder.field("keep_none_chinese_in_joined_full_pinyin", true);
                        }
                        builder.endObject();
                        builder.startObject("ik_smart_tokenizer");
                        {
                            builder.field("type", "ik_smart");
                        }
                        builder.endObject();
                        builder.startObject("ik_max_tokenizer");
                        {
                            builder.field("type", "ik_max_word");
                        }
                        builder.endObject();
                        builder.startObject("ik_smart_pinyin_tokenizer");
                        {
                            builder.field("type", "ik_smart");
                        }
                        builder.endObject();
                        builder.startObject("ik_max_pinyin_tokenizer");
                        {
                            builder.field("type", "ik_max_word");
                        }
                        builder.endObject();
                    }
                    builder.endObject();
                    builder.startObject("filter");
                    {
                        builder.startObject("pinyin_first_letter_and_full_pinyin_filter");
                        {
                            builder.field("type", "pinyin");
                            builder.field("keep_first_letter", true);  //设置为true话如：张三 会分词为 zs
                            builder.field("keep_separate_first_letter", false); //设置为true的话每个字的首字母都会进行分词
                            builder.field("keep_full_pinyin", true);
                            builder.field("keep_original", true);
                            builder.field("limit_first_letter_length", 16);
                            builder.field("lowercase", true);
                            builder.field("remove_duplicated_term", true);
                        }
                        builder.endObject();

                    }
                    builder.endObject();
                }
                builder.endObject();
            }
            builder.endObject();
        }
        builder.endObject();

        return builder;
    }

    //设置字段模板
    private XContentBuilder getElasticSearchMapping() throws IOException {
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.startArray("dynamic_templates");
            {

                builder.startObject();
                {
                    builder.startObject("date_fields");
                    {
                        builder.field("match", "date*");
                        builder.startObject("mapping");
                        {
                            builder.field("type", "date");
                            builder.field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||yyyy-MM||yyyy||epoch_millis");
                        }
                        builder.endObject();
                    }
                    builder.endObject();
                }
                builder.endObject();

                builder.startObject();
                {
                    builder.startObject("int_fields");
                    {
                        builder.field("match", "int*");
                        builder.startObject("mapping");
                        {
                            builder.field("type", "long");
                            builder.startObject("fields");
                            {
                                builder.startObject("keyword");
                                {
                                    builder.field("type", "keyword");
                                    builder.field("ignore_above", "256");
                                }
                                builder.endObject();
                            }
                            builder.endObject();
                        }
                        builder.endObject();
                    }
                    builder.endObject();
                }
                builder.endObject();

                builder.startObject();
                {
                    builder.startObject("string_fields");
                    {
                        builder.field("match", "string*");
                        builder.startObject("mapping");
                        {
                            builder.field("type", "text");
                            builder.field("search_analyzer", "hanlp_smart_analyzer");
                            builder.field("analyzer", "hanlp_max_analyzer");
                            builder.startObject("fields");
                            {
                                builder.startObject("pinyin");
                                {
                                    builder.field("type", "text");
                                    builder.field("analyzer", "ik_smart_pinyin_analyzer");
                                }
                                builder.endObject();
                                builder.startObject("full_pinyin");
                                {
                                    builder.field("type", "text");
                                    builder.field("analyzer", "keyword_full_pinyin_analyzer");
                                }
                                builder.endObject();
                                builder.startObject("first_pinyin");
                                {
                                    builder.field("type", "text");
                                    builder.field("analyzer", "keyword_first_pinyin_analyzer");
                                }
                                builder.endObject();
                                builder.startObject("keyword");
                                {
                                    builder.field("type", "keyword");
                                    builder.field("ignore_above", "256");
                                }
                                builder.endObject();
                            }
                            builder.endObject();
                        }
                        builder.endObject();
                    }
                    builder.endObject();
                }
                builder.endObject();

//               builder.startObject();
//                {
//                    builder.startObject("other_fields");
//                    {
//                        builder.field("match", "*");
//                        builder.field("unmatch", "op_*");
//                        builder.startObject("mapping");
//                        {
//                            builder.field("type", "text");
//                            builder.field("search_analyzer", "hanlp_smart_analyzer");
//                            builder.field("analyzer", "hanlp_max_analyzer");
//                            builder.startObject("fields");
//                            {
//                                builder.startObject("pinyin");
//                                {
//                                    builder.field("type", "text");
//                                    builder.field("analyzer", "ik_smart_pinyin_analyzer");
//                                }
//                                builder.endObject();
//                                builder.startObject("keyword");
//                                {
//                                    builder.field("type", "keyword");
//                                    builder.field("ignore_above", "256");
//                                }
//                                builder.endObject();
//                            }
//                            builder.endObject();
//                        }
//                        builder.endObject();
//                    }
//                    builder.endObject();
//                }
//                builder.endObject();

            }
            builder.endArray();
        }
        builder.endObject();

        return builder;
    }

}
